{
  "nbformat_minor": 0,
  "nbformat": 4,
  "cells": [
    {
      "source": [
        "#### Copyright 2017 Google LLC."
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "JndnmDMp66FL"
      }
    },
    {
      "source": [
        "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "hMqWDc_m6rUC",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        },
        "cellView": "both"
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        " # Introducci\u00f3n r\u00e1pida a Pandas"
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "rHLcriKWLRe4"
      }
    },
    {
      "source": [
        "**Objetivos de aprendizaje:**\n",
        "  * Obtener una introducci\u00f3n a las estructuras de datos de `DataFrame` y `Series` de la biblioteca de *Pandas*\n",
        "  * Acceder y manipular datos dentro de `DataFrame` y `Series`\n",
        "  * Importar datos CSV a un `DataFrame` de *Pandas*\n",
        "  * Reindexar un `DataFrame` para obtener datos aleatorios"
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "QvJBqX8_Bctk"
      }
    },
    {
      "source": [
        " [*pandas*](http://pandas.pydata.org/) es una API de an\u00e1lisis de datos en columnas, ideal para manipular y analizar datos de entrada. Adem\u00e1s, muchos marcos de trabajo de AA admiten las estructuras de datos *pandas* como entradas.\n",
        "Si bien una introducci\u00f3n detallada a la API de *pandas* abarcar\u00eda muchas p\u00e1ginas, los conceptos principales que presentamos a continuaci\u00f3n son simples. Para obtener una referencia m\u00e1s completa, el [sitio de documentaci\u00f3n de *pandas*](http://pandas.pydata.org/pandas-docs/stable/index.html) incluye una documentaci\u00f3n exhaustiva y numerosos instructivos."
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "TIFJ83ZTBctl"
      }
    },
    {
      "source": [
        " ## Conceptos b\u00e1sicos\n",
        "\n",
        "La siguiente l\u00ednea importa la API de *pandas* e imprime la versi\u00f3n de la API:"
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "s_JOISVgmn9v"
      }
    },
    {
      "source": [
        "from __future__ import print_function\n\n",
        "import pandas as pd\n",
        "pd.__version__"
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "aSRYu62xUi3g",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        " Las estructuras de datos principales en *pandas* est\u00e1n implementadas en dos clases:\n",
        "\n",
        "  * **`DataFrame`**, que puedes imaginar como una tabla de datos relacional, con filas y columnas con nombre.\n",
        "  * **`Series`**, que es una columna simple. Una clase `DataFrame` incluye una o m\u00e1s `Series` y un nombre para cada `Series`.\n",
        "\n",
        "El marco de datos es una abstracci\u00f3n que se usa normalmente para manipular datos. Hay implementaciones similares en [Spark](https://spark.apache.org/) y [R](https://www.r-project.org/about.html)."
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "daQreKXIUslr"
      }
    },
    {
      "source": [
        " Una manera de crear una `Series` es construir un objeto de `Series`. Por ejemplo:"
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "fjnAk1xcU0yc"
      }
    },
    {
      "source": [
        "pd.Series(['San Francisco', 'San Jose', 'Sacramento'])"
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "DFZ42Uq7UFDj",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        " Los objetos de `DataFrame` pueden crearse al enviar un `dict` que asigne nombres de columnas de `string` a sus `Series` correspondientes. Si las `Series` no coinciden con la longitud, los valores que falten se completan con valores [NA/NaN](http://pandas.pydata.org/pandas-docs/stable/missing_data.html) especiales. Ejemplo:"
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "U5ouUp1cU6pC"
      }
    },
    {
      "source": [
        "city_names = pd.Series(['San Francisco', 'San Jose', 'Sacramento'])\n",
        "population = pd.Series([852469, 1015785, 485199])\n",
        "\n",
        "pd.DataFrame({ 'City name': city_names, 'Population': population })"
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "avgr6GfiUh8t",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        " Pero por lo general, cargas un archivo completo en un `DataFrame`. El siguiente ejemplo carga un archivo con datos de viviendas de California. Ejecuta la siguiente celda para cargar los datos y crear definiciones de funciones:"
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "oa5wfZT7VHJl"
      }
    },
    {
      "source": [
        "california_housing_dataframe = pd.read_csv(\"https://download.mlcc.google.com/mledu-datasets/california_housing_train.csv\", sep=\",\")\n",
        "california_housing_dataframe.describe()"
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "av6RYOraVG1V",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        " En el ejemplo de arriba, se us\u00f3 `DataFrame.describe` para mostrar estad\u00edsticas interesantes sobre un `DataFrame`. Otra funci\u00f3n \u00fatil es `DataFrame.head`, que muestra los primeros registros de un `DataFrame`:"
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "WrkBjfz5kEQu"
      }
    },
    {
      "source": [
        "california_housing_dataframe.head()"
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "s3ND3bgOkB5k",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        " Otra funci\u00f3n util de *pandas* es la generaci\u00f3n de gr\u00e1ficos. Por ejemplo, `DataFrame.hist` permite estudiar r\u00e1pidamente la distribuci\u00f3n de los valores en una columna:"
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "w9-Es5Y6laGd"
      }
    },
    {
      "source": [
        "california_housing_dataframe.hist('housing_median_age')"
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "nqndFVXVlbPN",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        " ## Acceso a los datos\n",
        "\n",
        "Puedes acceder a los datos de `DataFrame` mediante las operaciones convencionales de dict/list de Python:"
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "XtYZ7114n3b-"
      }
    },
    {
      "source": [
        "cities = pd.DataFrame({ 'City name': city_names, 'Population': population })\n",
        "print(type(cities['City name']))\n",
        "cities['City name']"
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "_TFm7-looBFF",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        "print(type(cities['City name'][1]))\n",
        "cities['City name'][1]"
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "V5L6xacLoxyv",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        "print(type(cities[0:2]))\n",
        "cities[0:2]"
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "gcYX1tBPugZl",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        " Adem\u00e1s, *pandas* proporciona una API muy enriquecida para una [indexaci\u00f3n y selecci\u00f3n](http://pandas.pydata.org/pandas-docs/stable/indexing.html) avanzadas, que es un tema demasiado amplio como para cubrirlo aqu\u00ed."
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "65g1ZdGVjXsQ"
      }
    },
    {
      "source": [
        " ## Manipulaci\u00f3n de datos\n",
        "\n",
        "Puedes aplicar operaciones aritm\u00e9ticas b\u00e1sicas de Python a las `Series`. Por ejemplo:"
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "RM1iaD-ka3Y1"
      }
    },
    {
      "source": [
        "population / 1000."
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "XWmyCFJ5bOv-",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        " [NumPy](http://www.numpy.org/) es un kit de herramientas popular para el c\u00e1lculo cient\u00edfico. Las `Series` de *pandas* pueden usarse como argumentos para la mayor\u00eda de las funciones NumPy:"
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "TQzIVnbnmWGM"
      }
    },
    {
      "source": [
        "import numpy as np\n",
        "\n",
        "np.log(population)"
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "ko6pLK6JmkYP",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        " Para obtener informaci\u00f3n sobre transformaciones m\u00e1s complejas de una sola columna, puedes usar `Series.apply`. Al igual que la funci\u00f3n [funci\u00f3n map](https://docs.python.org/2/library/functions.html#map) de Python, `Series.apply` acepta como argumento una funci\u00f3n [funci\u00f3n lambda](https://docs.python.org/2/tutorial/controlflow.html#lambda-expressions), que se aplica a cada valor.\n",
        "\n",
        "El siguiente ejemplo crea una nueva `Series` que indica si la `population` es superior a un mill\u00f3n:"
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "xmxFuQmurr6d"
      }
    },
    {
      "source": [
        "population.apply(lambda val: val > 1000000)"
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Fc1DvPAbstjI",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        " \n",
        "Modificar `DataFrames` tambi\u00e9n es simple. Por ejemplo, el siguiente c\u00f3digo agrega dos `Series` a un `DataFrame` existente:"
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "ZeYYLoV9b9fB"
      }
    },
    {
      "source": [
        "cities['Area square miles'] = pd.Series([46.87, 176.53, 97.92])\n",
        "cities['Population density'] = cities['Population'] / cities['Area square miles']\n",
        "cities"
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "0gCEX99Hb8LR",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        " ## Ejercicio n.\u00ba 1\n",
        "\n",
        "Para modificar la tabla de `cities`, agrega una nueva columna booleana que sea Verdadera si y solo si *ambos* de los siguientes valores son Verdaderos:\n",
        "\n",
        "  * La ciudad le debe su nombre a un santo.\n",
        "  * La ciudad tiene un \u00e1rea superior a 50 millas cuadradas.\n",
        "\n",
        "**Nota:** Las `Series` booleanas se combinan en funci\u00f3n de los bits, en lugar de los operadores booleanos tradicionales. Por ejemplo, cuando utilices *logical and*, usa `&` en lugar de `and`.\n",
        "\n",
        "**Hint:** \"San\" en espa\u00f1ol representa \"santo\"."
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "6qh63m-ayb-c"
      }
    },
    {
      "source": [
        "# Your code here"
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "zCOn8ftSyddH",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        " ### Soluci\u00f3n\n",
        "\n",
        "Haz clic a continuaci\u00f3n para obtener una soluci\u00f3n."
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "YHIWvc9Ms-Ll"
      }
    },
    {
      "source": [
        "cities['Is wide and has saint name'] = (cities['Area square miles'] > 50) & cities['City name'].apply(lambda name: name.startswith('San'))\n",
        "cities"
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "T5OlrqtdtCIb",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        " ## \u00cdndices\n",
        "Los objetos `Series` y `DataFrame` tambi\u00e9n definen una propiedad de `index` que asigna un valor de identificador a cada elemento `Series` o fila `DataFrame`.\n",
        "\n",
        "De forma predeterminada, en la construcci\u00f3n, *pandas* asigna valores de \u00edndice que reflejan la solicitud de los datos de origen. Una vez creados, los valores de \u00edndice son estables, es decir, no cambian cuando cambia el orden de los datos."
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "f-xAOJeMiXFB"
      }
    },
    {
      "source": [
        "city_names.index"
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "2684gsWNinq9",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        "cities.index"
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "F_qPe2TBjfWd",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        " Llama `DataFrame.reindex` para cambiar el orden de las filas de forma manual. Por ejemplo, la siguiente acci\u00f3n tiene el mismo efecto que ordenar los valores por nombre de ciudad:"
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "hp2oWY9Slo_h"
      }
    },
    {
      "source": [
        "cities.reindex([2, 0, 1])"
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "sN0zUzSAj-U1",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        " La reindexaci\u00f3n es una excelente manera de seleccionar un `DataFrame` de forma aleatoria. En el ejemplo que se muestra a continuaci\u00f3n, tomamos el \u00edndice, que es del tipo matriz, y lo enviamos a la funci\u00f3n `random.permutation` de NumPy, que selecciona sus valores de forma aleatoria. Utilizar la `reindexaci\u00f3n` con esta matriz aleatoria provoca que las filas de `DataFrame` se seleccionen de forma aleatoria de la misma manera.\n",
        "\u00a1Prueba ejecutar la siguiente celda varias veces!"
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "-GQFz8NZuS06"
      }
    },
    {
      "source": [
        "cities.reindex(np.random.permutation(cities.index))"
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "mF8GC0k8uYhz",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        " Para obtener m\u00e1s informaci\u00f3n, consulta [Documentaci\u00f3n de \u00edndice](http://pandas.pydata.org/pandas-docs/stable/indexing.html#index-objects)."
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "fSso35fQmGKb"
      }
    },
    {
      "source": [
        " ## Ejercicio n.\u00ba 2\n",
        "\n",
        "El m\u00e9todo de `reindex` permite los valores de \u00edndice que no est\u00e1n en los valores de \u00edndice originales de `DataFrame`. Pru\u00e9balo y observa lo que sucede si usas esos valores. \u00bfPor qu\u00e9 supones que los permite?"
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "8UngIdVhz8C0"
      }
    },
    {
      "source": [
        "# Your code here"
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "PN55GrDX0jzO",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        " ### Soluci\u00f3n\n",
        "\n",
        "Haz clic a continuaci\u00f3n para conocer la soluci\u00f3n."
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "TJffr5_Jwqvd"
      }
    },
    {
      "source": [
        " Si la matriz de la entrada `reindex` incluye valores que no se encuentran en los valores de \u00edndice originales `DataFrame`, `reindex` agregar\u00e1 nuevas filas para esos \u00edndices \"faltantes\" y completar\u00e1 todas las columnas correspondientes con los valores `NaN`:"
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "8oSvi2QWwuDH"
      }
    },
    {
      "source": [
        "cities.reindex([0, 4, 5, 2])"
      ],
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "yBdkucKCwy4x",
        "colab": {
          "autoexec": {
            "wait_interval": 0,
            "startup": false
          }
        }
      },
      "outputs": [],
      "execution_count": 0
    },
    {
      "source": [
        " Este comportamiento es util, ya que por lo general los \u00edndices son strings extra\u00eddos de los datos actuales (consulta la [*pandas* reindex\n",
        "documentation](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.reindex.html) para obtener un ejemplo en el que los valores de \u00edndice son nombres de navegadores).\n",
        "\n",
        "En este caso, permitir los \u00edndices \"faltantes\" facilita la reindexaci\u00f3n mediante una lista externa, dado que no tienes que preocuparte por sanear directamente las entradas."
      ],
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "2l82PhPbwz7g"
      }
    }
  ],
  "metadata": {
    "colab": {
      "default_view": {},
      "version": "0.3.2",
      "collapsed_sections": [
        "JndnmDMp66FL",
        "YHIWvc9Ms-Ll",
        "TJffr5_Jwqvd"
      ],
      "name": "intro_to_pandas.ipynb",
      "views": {}
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    }
  }
}
